
package Procesos;
import Interfaz.VentanaPrincipal;
import java.awt.Color;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import Excepciones.ErrorValidacion;
import negocio.AtributoXML;
import negocio.EtiquetaXML;
import negocio.Linea;
/**
 *  Esta clase contiene los metodos validadores del xml
 * @author Emiliano
 */
public class Validaciones {
    public static ArrayList<EtiquetaXML> conjuntoEtiquetas = null;
    public static boolean comentario;
    public static boolean comentarioFinal;
    public static boolean validacionCorrecta;
    public static boolean resultadoValidacionDeclaracionXML;
    public static boolean resultadoValidacionDeclaracionDocType;
    public static Linea lineaDeclaracion;
    public static String declaracionXML = "";
    public static String declaracionDocType = "";

    /**
     * Este metodo realiza la validacion del XML. En el caso que la validacion sea correcta
     * devuelve true y de lo contrario devuelve false
     * @param xml - Es un String que contiene todo el contenido del xml que se va validar
     * @return true en el caso que la validacion sea correcta - false en el caso contrario
     */
    public static boolean validarXML(String xml) {
        try {
            int numeroRenglon = 0;
            //limpia la consola
            VentanaPrincipal.imprimirMensajeConsola("");
            Validaciones.inicializar();

            // Se divide el contenido total del xml en renglones
            String renglonesXML[] = xml.split("\\n");
            for (int i = 0; i < renglonesXML.length; i++) {
                String lineXML = renglonesXML[i];

                numeroRenglon++;
                Linea linea = new Linea(lineXML, numeroRenglon);

                Validaciones.validarComentarios(linea);

                // Si la linea es un comentario no se deben realizar las validaciones
                if (!Validaciones.comentario) {
                    /* En el caso que la linea no sea un comentario, primero verifico si la linea
                     * corresponde a una declaracion xml o doctype
                     */
                    boolean esDeclaracionXML = Validaciones.validarDeclaracionXML(linea);
                    //guarda la linea declaración en lineaDeclaracion
                    if (esDeclaracionXML) {
                        lineaDeclaracion = linea;
                    }

                    boolean esDeclaracionDocType = false;
                    if (!esDeclaracionXML) {                //si no es una declaracion XML valida si es una declaracion DocType
                        esDeclaracionDocType = Validaciones.validarDeclaracionDocType(linea);
                    } else {
                        declaracionXML = linea.getLine();
                    }
                    if (esDeclaracionDocType) {
                        declaracionDocType = linea.getLine();
                    }
                    if (!esDeclaracionXML && !esDeclaracionDocType) {
                        /* En el caso que la linea no sea una declaracion XML o DocType se realizan
                         * las validaciones
                         */
                        Validaciones.validarEtiqueta(linea);
                        Validaciones.validarLimpieza(linea);
                        Validaciones.armarConjuntosAperturasCierres(linea);
                    }
                    esDeclaracionXML = false;
                    esDeclaracionDocType = false;
                }

                if (Validaciones.comentarioFinal) {
                    Validaciones.comentario = false;
                    Validaciones.comentarioFinal = false;
                }
            }

            /* Se valida que los conjuntos de pares de etiquetas de aperturas y cierres
             * esten cerrados
             */
            Validaciones.validarAperturaCierreEtiquetas();

            if (Validaciones.validacionCorrecta) {
                /* En el caso que la validacion del xml sea correcta se procede a armar
                 * la estructura de entidades
                 */
                GestorEntidad.armarEntidades(Validaciones.conjuntoEtiquetas);
            }

        } catch (ErrorValidacion e) {
            validacionCorrecta = false;
            //Captura las excepciones que lanzan los metodos de la clase Validaciones por mal formato xml
            mostrarMensajeErrorValidacion(e);
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("error " + e);
        }

        return Validaciones.validacionCorrecta;
    }

    /**
     * Metodo utilizado para la validacion de las etiquetas(tags) xml
     * @param linea
     */
    public static void validarEtiqueta(Linea linea) throws ErrorValidacion {
        // <(.*)>(.*)<(/*)(.*)>
        Matcher matcherEtiquetaDoble = obtenerMatcher(RegularExpresions.etiquetaDobleValidar, linea.getLine());
        boolean esEtiquetaDoble = matcherEtiquetaDoble.find();

        if (esEtiquetaDoble) {
            /* Si entra en este if, es porque hay una etiqueta doble en la linea (ej: <persona> </persona>)
             */

            String grupo3 = matcherEtiquetaDoble.group(3);

            if (!grupo3.equals("/")) { // se comprueba que la segunda etiqueta sea una etiqueta de cierre
                ErrorValidacion error = new ErrorValidacion(ErrorValidacion.ERROR_VALIDACION_ETIQUETA, grupo3, linea, "Falta agregar el simbolo \"/\" en la segunda etiqueta");
                throw error;
                
            }
        } else {
            /* En esta rama del if se analiza si tengo una sola etiqueta en la linea
            Primero analizo si en la linea hay algun patron que indique la presencia
            de una etiqueta de la forma "< >"
             */
            // <(.*)>
            Matcher matcherEtiquetaSimple = obtenerMatcher(RegularExpresions.etiquetaSimple, linea.getLine());
            boolean esEtiquetaSimple = matcherEtiquetaSimple.find();

            if (esEtiquetaSimple) {
                /* Si entra en esta rama es porque la linea contiene una etiqueta del tipo
                 * <hola>
                 * </hola>
                 */

                // "<(/*)(\\w|\\.|-)*>" <persona> ; </persona>
                Matcher matcherA = obtenerMatcher(RegularExpresions.etiquetaSimpleA, linea.getLine());
                boolean a = matcherA.find();

                if (a) {
                    /* Si entra en esta seccion es porque tenemos una etiqueta del tipo
                     * <persona> o </persona> y por lo tanto se debe validar que haya
                     * un simbolo / o que no haya nada en dicho grupo
                     */
                    String grupo1 = matcherA.group(1);
                    if (!grupo1.equals("/") && !grupo1.equals("")) {
                        
                        ErrorValidacion error = new ErrorValidacion(ErrorValidacion.ERROR_VALIDACION_ETIQUETA, grupo1, linea, "Simbolo incorrecto en la etiqueta");
                        throw error;
                    }
                } else {
                    /*
                     * En esta parte del if analizo si la etiqueta es del tipo
                     * <hola/>                                                      ELEMENTO VACIO!!!
                     */

                    // "<(\\w*\\s*)(/)>" <persona/>
                    Matcher matcherB = obtenerMatcher(RegularExpresions.etiquetaSimpleB, linea.getLine());
                    boolean b = matcherB.find();

                    if (b) {
                        /* Si entra en esta seccion es porque tenemos una etiqueta del tipo
                         * <persona/> y por lo tanto se debe validar que haya
                         * un simbolo / o que no haya nada en dicho grupo
                         */
                        String grupo2 = matcherB.group(2);
                        if (!grupo2.equals("/") && !grupo2.equals("")) {
                            
                            ErrorValidacion error = new ErrorValidacion(ErrorValidacion.ERROR_VALIDACION_ETIQUETA, grupo2, linea, null);
                            throw error;
                        }
                    } else {
                        /*
                         * En esta parte del else analizo si la etiqueta es del tipo
                         * <capitulo nombre="inicial" parrafos="20" />
                         */

                        // "<(\\?)*((\\w|\\.|-)*(\\s*\\w*-?\\w*=\".*\\s*\\w*\")*\\s*)(/*)>"  <capitulo nombre="inicial" parrafos="20" />
                        Matcher matcherC = obtenerMatcher(RegularExpresions.etiquetaSimpleC, linea.getLine());
                        boolean c = matcherC.find();

                        if (c) {
                            /* Si entra en esta seccion es porque tenemos una etiqueta del tipo
                             *  <capitulo nombre="inicial" parrafos="20" /> y por lo tanto se debe validar que la
                             * etiqueta termine con el simbolo /
                             */
                            String grupo5 = matcherC.group(5);
                            if (!grupo5.equals("/") && !grupo5.equals("")) {
                                
                                ErrorValidacion error = new ErrorValidacion(ErrorValidacion.ERROR_VALIDACION_ETIQUETA, grupo5, linea, null);
                                throw error;
                            }
                        } else {
                            /*
                             * Si la etiqueta entra en esta parte es porque no se corresponde
                             * con ninguno de los casos anteriores y por lo tanto la etiqueta
                             * tiene un error
                             */
                            
                            ErrorValidacion error = new ErrorValidacion(ErrorValidacion.ERROR_VALIDACION_ETIQUETA, null, linea, "El formato de la linea es incorrecto!");
                            throw error;
                        }
                    }
                }
            } else {
                /*
                 * En esta seccion valido que la linea no sea una etiqueta,
                 * no debe contener un simbolo < o >
                 */

                // (>+)
                Matcher matcherSimboloMayor = obtenerMatcher(RegularExpresions.simboloMayor, linea.getLine());
                boolean haySimboloMayor = matcherSimboloMayor.find();
                if (haySimboloMayor) {
                    String grupo = matcherSimboloMayor.group(1);
                    
                    ErrorValidacion error = new ErrorValidacion(ErrorValidacion.ERROR_GENERAL, grupo, linea, "Hay un simbolo > en la linea");
                    throw error;
                }

                // (<+)
                Matcher matcherSimboloMenor = obtenerMatcher(RegularExpresions.simboloMenor, linea.getLine());
                boolean haySimboloMenor = matcherSimboloMenor.find();
                if (haySimboloMenor) {
                    String grupo = matcherSimboloMenor.group(1);
                    
                    ErrorValidacion error = new ErrorValidacion(ErrorValidacion.ERROR_VALIDACION_ETIQUETA, grupo, linea, "Hay un simbolo < en la linea");
                    throw error;
                }
            }
        }
    }

    /**
     * Esta funcion es utilizada para comprobar si una etiqueta esta limpia
     * por delante y detras. Primero se verifica si es una etiqueta doble
     * (ej: <persona> </persona> ) y en caso contrario se comprueba si la linea es
     * una etiqueta simple (ej: <persona>, <persona/>).
     * @param linea - el renglon que se va analizar
     */
    public static void validarLimpieza(Linea linea) throws ErrorValidacion {
        // RegularExpresions.etiquetaDobleLimpieza = (.*)<(.*)>(.*)<(.*)>(.*)
        Matcher matcherEtiquetaDoble = obtenerMatcher(RegularExpresions.etiquetaDobleLimpieza, linea.getLine());
        boolean encontroPatronEtiquetaDoble = matcherEtiquetaDoble.find();

        if (encontroPatronEtiquetaDoble) {
            /* Si entra en este if es porque la etiqueta es doble
             */
            String grupo1 = matcherEtiquetaDoble.group(1);
            String grupo5 = matcherEtiquetaDoble.group(5);

            if (!grupo1.equals("")) {
                /* Aca lo que hacemos es validar que haya solamente
                 * espacios en blanco antes de la etiqueta
                 */
                Matcher noEspacio = obtenerMatcher(RegularExpresions.caracterNoEspacio, grupo1);
                boolean encontroCaracterNoEspacio = noEspacio.find();
                if (encontroCaracterNoEspacio) {
                    
                    ErrorValidacion error = new ErrorValidacion(ErrorValidacion.ERROR_LIMPIEZA, grupo1, linea, "No puede haber caracteres a la izquierda de la etiqueta");
                    throw error;
                }
            }

            if (!grupo5.equals("")) {
                /* Aca lo que hacemos es validar que haya solamente
                 * espacios en blanco antes de la etiqueta
                 */
                Matcher noEspacio = obtenerMatcher(RegularExpresions.caracterNoEspacio, grupo5);
                boolean encontroCaracterNoEspacio = noEspacio.find();
                if (encontroCaracterNoEspacio) {
                    
                    ErrorValidacion error = new ErrorValidacion(ErrorValidacion.ERROR_LIMPIEZA, grupo5, linea, "No puede haber caracteres a la derecha de la etiqueta");
                    throw error;
                }
            }
        } else {
            Matcher matcherEtiquetaSimple = obtenerMatcher("(.*)<(/*)(.*)>(.*)", linea.getLine());
            boolean encontroPatronEtiquetaSimple = matcherEtiquetaSimple.find();

            if (encontroPatronEtiquetaSimple) {
                String grupo1 = matcherEtiquetaSimple.group(1);
                String grupo2 = matcherEtiquetaSimple.group(2);
                String grupo4 = matcherEtiquetaSimple.group(4);

                if (grupo2.equals("")) {
                    /* Si entra por aca es una etiqueta de apertura del tipo <persona>
                     * por lo tanto se debe validar que no haya simbolos a la derecha
                     */

                    if (!grupo1.equals("")) {
                        /* Aca lo que hacemos es validar que haya solamente
                         * espacios en blanco antes de la etiqueta
                         */
                        Matcher noEspacio = obtenerMatcher(RegularExpresions.caracterNoEspacio, grupo1);
                        boolean encontroCaracterNoEspacio = noEspacio.find();
                        if (encontroCaracterNoEspacio) {
                            
                            ErrorValidacion error = new ErrorValidacion(ErrorValidacion.ERROR_LIMPIEZA, grupo1, linea, "No puede haber caracteres a la izquierda de la etiqueta");
                            throw error;
                        }
                    }
                } else if (grupo2.equals("/")) { // si entra por aca es una etiqueta del tipo </persona>
                    if (!grupo4.equals("")) {
                        /* Aca lo que hacemos es validar que haya solamente
                         * espacios en blanco despues de la etiqueta
                         */
                        Matcher noEspacio = obtenerMatcher(RegularExpresions.caracterNoEspacio, grupo4);
                        boolean encontroCaracterNoEspacio = noEspacio.find();
                        if (encontroCaracterNoEspacio) {
                            
                            ErrorValidacion error = new ErrorValidacion(ErrorValidacion.ERROR_LIMPIEZA, grupo4, linea, "No puede haber caracteres a la derecha de la etiqueta");
                            throw error;
                        }
                    }
                }
            }
        }
    }

    /**
     * Funcion utilizada para verificar si una linea es un comentario
     * @param linea - el renglon que se va analizar
     * @throws LineaException
     */
    public static void validarComentarios(Linea linea) throws ErrorValidacion {
        Matcher matcher;
        boolean encontroPatron = false;

        /* En esta parte lo que hacemos es ver si la linea se corresponde
         * con un comentario inicial "<!--"
         */

        // (.*)<!--(.*)
        matcher = obtenerMatcher(RegularExpresions.comentarioInicial, linea.getLine());
        encontroPatron = matcher.find();

        if (encontroPatron) {
            String grupo1 = matcher.group(1);
            if (!grupo1.equals("")) {
                /* Aca lo que hacemos es validar que haya solamente
                 * espacios en blanco antes de la etiqueta
                 */
                Matcher noEspacio = obtenerMatcher(RegularExpresions.caracterNoEspacio, grupo1);
                boolean encontroCaracterNoEspacio = noEspacio.find();

                if (encontroCaracterNoEspacio) {
                    
                    ErrorValidacion error = new ErrorValidacion(ErrorValidacion.ERROR_COMENTARIOS, grupo1, linea, "No puede haber caracteres a la izquierda de la etiqueta");
                    throw error;
                } else {
                    Validaciones.comentario = true;
                }
            } else {
                Validaciones.comentario = true;
            }
        }

        /* En esta parte lo que hacemos es ver si la linea se corresponde
         * con comentario final "-->"
         */

        // (.*)-->(.*)
        matcher = obtenerMatcher(RegularExpresions.comentarioFinal, linea.getLine());
        encontroPatron = matcher.find();

        if (encontroPatron) {
            // Si se detecto un comentario final pero es de la forma "-->lala" hay que arrojar un error

            String grupo2 = matcher.group(2);
            if (!grupo2.equals("")) {
                /* Aca lo que hacemos es validar que haya solamente
                 * espacios en blanco despues de la etiqueta
                 */
                Matcher noEspacio = obtenerMatcher(RegularExpresions.caracterNoEspacio, grupo2);
                boolean encontroCaracterNoEspacio = noEspacio.find();

                if (encontroCaracterNoEspacio) {
                    //Error.mensajeError(Error.ERROR_COMENTARIOS, grupo2, linea, "No puede haber caracteres a la despues de la etiqueta");
                    ErrorValidacion error = new ErrorValidacion(ErrorValidacion.ERROR_COMENTARIOS, grupo2, linea, "No puede haber caracteres a la derecha de la etiqueta");
                    throw error;
                } else {
                    Validaciones.comentarioFinal = true;
                }
            } else {
                Validaciones.comentarioFinal = true;
            }
        }
    }

    /**
     * Metodo utilizado para validar que los pares de etiquetas del xml se corresponden.
     * Para ello se recorre el objeto conjuntoEtiquetas que contiene los pares de etiquetas
     * y se pregunta si alguno de estos pares no se encuentra cerrado.
     */
    public static void validarAperturaCierreEtiquetas() throws ErrorValidacion {
        for (int i = 0; i < conjuntoEtiquetas.size(); i++) {
            if (!conjuntoEtiquetas.get(i).isParesCerrados()) {
                
                ErrorValidacion error = new ErrorValidacion(ErrorValidacion.ERROR_APERTURA_CIERRE_ETIQUETAS, null, conjuntoEtiquetas.get(i).getEtiquetaApertura(), "EXISTE ALGUNA ETIQUETA QUE NO ESTA DEBIDAMENTE CERRADA");
                throw error;
            }
        }
    }

    /**
     * Metodo utilizado para armar el conjunto de pares de etiquetas
     * @param linea - el renglon que se va analizar
     */
    public static ArrayList<EtiquetaXML> armarConjuntosAperturasCierres(Linea linea) throws ErrorValidacion {
        // <(\\?)*((\\w|\\.|-)*)((\\s*\\w*-?\\w*=\"(\\w|\\.|-)*\\s*(\\w|\\.)*\")*\\s*)(/*)>(.*)
        Matcher matcherEtiquetaApertura = obtenerMatcher(RegularExpresions.etiquetaApertura, linea.getLine());
        // (.*)<(/)((\\w|\\.|-)*)>
        Matcher matcherEtiquetaCerradura = obtenerMatcher(RegularExpresions.etiquetaCerradura, linea.getLine());

        boolean matchEtiquetaApertura = matcherEtiquetaApertura.find();
        boolean matchEtiquetaCerradura = matcherEtiquetaCerradura.find();

        String apertura;
        String cierre;
        String contenido;

        if (matchEtiquetaApertura && matchEtiquetaCerradura) {
            /* Si entra por esta rama se analiza una etiqueta doble
             */
            // <(.*)>(.*)</(.*)>
            Matcher matcherEtiquetaDoble = obtenerMatcher(RegularExpresions.etiquetaDobleValidar2, linea.getLine());
            boolean matchEtiquetaDoble = matcherEtiquetaDoble.find();

            if (matchEtiquetaDoble) {
                String grupo1 = matcherEtiquetaDoble.group(1);
                String grupo2 = matcherEtiquetaDoble.group(2);
                String grupo3 = matcherEtiquetaDoble.group(3);

                // Se debe validar que las dos etiquetas sean iguales
                if (grupo1.equals(grupo3)) {
                    /* Si entra en este if, las etiquetas de apertura y cierre son iguales
                     * Se crea el objeto EtiquetaXML y se cierra el par de etiquetas
                     */

                    EtiquetaXML etiqueta = new EtiquetaXML();
                    apertura = grupo1;
                    cierre = grupo3;
                    contenido = grupo2;

                    etiqueta.setEtiquetaApertura(linea);
                    etiqueta.setEtiquetaCierre(linea);
                    etiqueta.setApertura(apertura);
                    etiqueta.setCierre(cierre);
                    etiqueta.setParesCerrados(true);
                    etiqueta.setContenidoEtiqueta(contenido);
                    if (conjuntoEtiquetas.isEmpty()) {
                        /* Si conjuntoEtiquetas esta vacio entonces es porque no se guardo
                         * ninguna etiqueta anteriormente y por ende corresponde a la raiz
                         */
                        etiqueta.setEsRaiz(true);
                    } else {
                        /* Si la etiqueta no es una raiz, se debe setear la etiqueta padre
                         * correspondiente. Para ello se recorre el objeto conjuntoEtiquetas
                         * del ultimo al primer elemento y el primer elemento no nulo y que no tiene
                         * los pares cerrados corresponde a la etiqueta padre
                         */
                        for (int i = conjuntoEtiquetas.size() - 1; i >= 0; i--) {
                            if (conjuntoEtiquetas.get(i).getApertura() != null && conjuntoEtiquetas.get(i).isParesCerrados() == false) {
                                etiqueta.setEtiquetaPadre((EtiquetaXML) conjuntoEtiquetas.get(i));
                                break;
                            }
                        }
                    }
                    conjuntoEtiquetas.add(etiqueta);
                } else {
                    /* En el caso que las etiquetas no sean iguales se entra por esta rama
                     * y se arroja el mensaje de error
                     */
                    //Error.mensajeError(Error.ERROR_APERTURA_CIERRE_ETIQUETAS, null, linea, "La etiqueta de apertura debe ser igual a la de cierre");
                    ErrorValidacion error = new ErrorValidacion(ErrorValidacion.ERROR_APERTURA_CIERRE_ETIQUETAS, null, linea, "La etiqueta de apertura debe ser igual a la de cierre");
                    throw error;
                }
            }
        } else if (matchEtiquetaApertura) {
            /* Si entra por esta rama se analiza una etiqueta de apertura o una
             * etiqueta del tipo <persona/>
             */

            // <(\\?)*((\\w|\\.|-)*)((\\s*\\w*-?\\w*=\"(\\w|\\.|-)*\\s*(\\w|\\.)*\")*\\s*)(/*)>(.*)
            String grupo8 = matcherEtiquetaApertura.group(8); // (/*) barra de cierre para etiquetas del tipo <persona/>
            String grupo2 = matcherEtiquetaApertura.group(2); // ((\\w|\\.|-)*) nombre de la etiqueta
            String grupo4 = matcherEtiquetaApertura.group(4); // ((\\s*\\w*-?\\w*=\"(\\w|\\.|-)*\\s*(\\w|\\.)*\")*\\s*) atributos de la etiqueta
            String grupo9 = matcherEtiquetaApertura.group(9); // >(.*) captura lo que se encuentra a la derecha de >

            if (!grupo8.equals("/")) {
                /* En esta rama del if se analizan las etiquetas de apertura que no son
                 * del tipo <persona/>
                 */

                EtiquetaXML etiqueta = new EtiquetaXML();
                apertura = grupo2;
                etiqueta.setEtiquetaApertura(linea);
                etiqueta.setApertura(apertura);
                etiqueta.setContenidoEtiqueta(grupo9);

                Matcher matcherAtributos = obtenerMatcher("((\\w|\\.|-)*)=\"(\\w*)\"", grupo4);
                while (matcherAtributos.find()) {
                    /* Si entra por este while es porque encontro un patron
                     * de atributos en la etiqueta
                     */
                    String atributoCompleto = matcherAtributos.group();
                    String nombreAtributo = matcherAtributos.group(1);
                    String valorAtributo = matcherAtributos.group(3);

                    AtributoXML atributo = new AtributoXML();
                    atributo.setAtributoCompleto(atributoCompleto);
                    atributo.setNombre(nombreAtributo);
                    atributo.setValor(valorAtributo);

                    etiqueta.getAtributos().add(atributo);
                }

                if (conjuntoEtiquetas.isEmpty()) {
                    etiqueta.setEsRaiz(true);
                } else {
                    boolean encontroPadre = false;
                    for (int i = conjuntoEtiquetas.size() - 1; i >= 0; i--) {
                        if (conjuntoEtiquetas.get(i).getApertura() != null && conjuntoEtiquetas.get(i).isParesCerrados() == false) {
                            etiqueta.setEtiquetaPadre((EtiquetaXML) conjuntoEtiquetas.get(i));
                            encontroPadre = true;
                            break;
                        }
                    }
                    if (!encontroPadre) {
                        ErrorValidacion error = new ErrorValidacion(ErrorValidacion.ERROR_GENERAL, null, linea, "NO PUEDE HABER MAS DE UNA RAIZ");
                        throw error;
                    }
                }
                conjuntoEtiquetas.add(etiqueta);
            } else if (grupo8.equals("/")) {
                // Aca se analizan las etiquetas del tipo <persona/>
                EtiquetaXML etiqueta = new EtiquetaXML();
                apertura = grupo2;
                cierre = grupo2;

                etiqueta.setEtiquetaApertura(linea);
                etiqueta.setEtiquetaCierre(linea);
                etiqueta.setApertura(apertura);
                etiqueta.setCierre(cierre);
                etiqueta.setParesCerrados(true);

                Matcher matcherAtributos = obtenerMatcher("((\\w|\\.|-)*)=\"(\\w*)\"", grupo4);
                while (matcherAtributos.find()) {
                    String atributoCompleto = matcherAtributos.group();
                    String nombreAtributo = matcherAtributos.group(1);
                    String valorAtributo = matcherAtributos.group(3);

                    AtributoXML atributo = new AtributoXML();
                    atributo.setAtributoCompleto(atributoCompleto);
                    atributo.setNombre(nombreAtributo);
                    atributo.setValor(valorAtributo);

                    etiqueta.getAtributos().add(atributo);
                }

                if (conjuntoEtiquetas.isEmpty()) {
                    /* Si conjuntoEtiquetas esta vacio entonces es porque no se guardo
                     * ninguna etiqueta anteriormente y por ende corresponde a la raiz
                     */
                    etiqueta.setEsRaiz(true);
                } else {
                    /* Si la etiqueta no es una raiz, se debe setear la etiqueta padre
                     * correspondiente. Para ello se recorre el objeto conjuntoEtiquetas
                     * del ultimo al primer elemento y el primer elemento no nulo y que tiene no
                     * los pares cerrados corresponde a la etiqueta padre
                     */
                    for (int i = conjuntoEtiquetas.size() - 1; i >= 0; i--) {
                        if (conjuntoEtiquetas.get(i).getApertura() != null && conjuntoEtiquetas.get(i).isParesCerrados() == false) {
                            etiqueta.setEtiquetaPadre((EtiquetaXML) conjuntoEtiquetas.get(i));
                            break;
                        }
                    }
                }
                conjuntoEtiquetas.add(etiqueta);
            }
        } else if (matchEtiquetaCerradura) {
            /* Si entra por esta rama se analiza una etiqueta de cierre
             */

            // (.*)<(/)((\\w|\\.|-)*)>
            cierre = matcherEtiquetaCerradura.group(3);
            contenido = matcherEtiquetaCerradura.group(1);
            boolean encontroEtiquetaApertura = false;

            /* Se debe recorrer conjuntoEtiquetas para encontrar la etiqueta de apertura
             */
            for (int i = conjuntoEtiquetas.size() - 1; i >= 0; i--) {
                if (conjuntoEtiquetas.get(i).getApertura() != null && conjuntoEtiquetas.get(i).isParesCerrados() == false) {
                    if (conjuntoEtiquetas.get(i).getApertura().equals(cierre)) {
                        /* El primer par de etiquetas que tiene:
                         * 	 - una etiqueta de apertura no nula
                         * 	 - el par no esta cerrado
                         *   - el nombre de la etiqueta de apertura es igual a la etiqueta de cierre
                         * corresponde al par buscado
                         */

                        conjuntoEtiquetas.get(i).setEtiquetaCierre(linea);
                        conjuntoEtiquetas.get(i).setParesCerrados(true);
                        conjuntoEtiquetas.get(i).setCierre(cierre);
                        conjuntoEtiquetas.get(i).setContenidoEtiqueta(conjuntoEtiquetas.get(i).getContenidoEtiqueta().concat(contenido));

                        encontroEtiquetaApertura = true;
                        break;
                    } else {
                        /* Si entra por esta rama es porque se encontro una etiqueta
                         * de apertura no nula y que no esta cerrada pero el nombre
                         * es distinto al de la etiqueta de cierre por lo tanto se
                         * debe lanzar un error
                         */

                        //Error.mensajeError(Error.ERROR_APERTURA_CIERRE_ETIQUETAS, null, linea, "La ultima etiqueta abierta no corresponde con la etiqueta de cierre");
                        ErrorValidacion error = new ErrorValidacion(ErrorValidacion.ERROR_APERTURA_CIERRE_ETIQUETAS, null, linea, "La ultima etiqueta abierta no corresponde con la etiqueta de cierre");
                        throw error;
                        //break;
                    }
                }
            }

            if (!encontroEtiquetaApertura) {
                //Error.mensajeError(Error.ERROR_APERTURA_CIERRE_ETIQUETAS, null, linea, "No se encontro ninguna etiqueta de apertura relacionada con la etiqueta de cierre");
                ErrorValidacion error = new ErrorValidacion(ErrorValidacion.ERROR_APERTURA_CIERRE_ETIQUETAS, null, linea, "No se encontro ninguna etiqueta de apertura relacionada con la etiqueta de cierre");
                throw error;
            }
        }

        if (!matchEtiquetaApertura && !matchEtiquetaCerradura) {
            /* Si la linea no corresponde con una etiqueta de apertura o de cierre
             * se debe verificar si es una linea suelta y se debe setear como contenido
             * de un par de etiquetas correspondiente
             */
            boolean encontroEtiquetaApertura = false;

            /*
            if (conjuntoEtiquetas.get(conjuntoEtiquetas.size()-1).getApertura() != null && conjuntoEtiquetas.get(conjuntoEtiquetas.size()-1).getEtiquetaCierre() == null ) {
            conjuntoEtiquetas.get(conjuntoEtiquetas.size()-1).setContenidoEtiqueta(linea.getLine());
            encontroEtiquetaApertura = true;
            }
            else {
            ErrorValidacion error = new ErrorValidacion(ErrorValidacion.ERROR_LINEA_SUELTA, null, linea, null);
            throw error;
            }*/

            for (int i = conjuntoEtiquetas.size() - 1; i >= 0; i--) {
                /* Recorremos conjuntoEtiquetas y buscamos la ultima etiqueta abierta
                 */
                if (conjuntoEtiquetas.get(i).getApertura() != null && conjuntoEtiquetas.get(i).getEtiquetaCierre() == null) {
                    conjuntoEtiquetas.get(i).setContenidoEtiqueta(linea.getLine());
                    encontroEtiquetaApertura = true;
                    break;
                }
            }

            if (!encontroEtiquetaApertura && !linea.getLine().equals("")) {
                /* Si no se encontro ninguna etiqueta de apertura para la linea suelta
                 * se debe lanzar un error
                 */
                //Error.mensajeError(Error.ERROR_LINEA_SUELTA, null, linea, null);
                ErrorValidacion error = new ErrorValidacion(ErrorValidacion.ERROR_LINEA_SUELTA, null, linea, null);
                throw error;
            }
        }

        return conjuntoEtiquetas;
    }

    /**
     * Metodo utilizado para saber si una linea es un espacio en blanco
     * @param linea - el renglon que se va analizar
     * @return boolean - true en el caso que la linea sea un espacio en blanco y false en caso contrario
     */
    public static boolean validarEspacioEnBlanco(Linea linea) {
        boolean esEspacioEnBlanco = true;
        //    "\\S+"; (linea en blanco)
        Matcher noEspacio = obtenerMatcher(RegularExpresions.caracterNoEspacio, linea.getLine());
        boolean encontroCaracterNoEspacio = noEspacio.find();
        if (encontroCaracterNoEspacio) {
            esEspacioEnBlanco = false;
        }

        return esEspacioEnBlanco;
    }

    /**
     * Esta funcion es utilizada para validar la declaracion inicial de un archivo XML
     * <?xml version="1.0" encoding="UTF-8" standalone="no"?>
     * @param linea - el renglon que se va analizar
     */
    public static boolean validarDeclaracionXML(Linea linea) throws ErrorValidacion {
        boolean esDeclaracionXML = false;

        if (!validarEspacioEnBlanco(linea)) {
            // RegularExpresions.declaracionXML = (.*)<\\?xml(.*)\\?>(.*)
            Matcher matcherDeclaracionXML = obtenerMatcher(RegularExpresions.declaracionXML, linea.getLine());
            boolean encontroPatronDeclaracionXML = matcherDeclaracionXML.find();

            if (encontroPatronDeclaracionXML) {
                /* Si entra en este if es porque la linea corresponde a una declaracion XML
                 */

                esDeclaracionXML = true;

                String grupo1 = matcherDeclaracionXML.group(1);
                String grupo3 = matcherDeclaracionXML.group(3);

                if (!grupo1.equals("")) {
                    /* Aca lo que hacemos es validar que haya solamente
                     * espacios en blanco antes de la declaracion
                     *                                                    "\\S+"          (linea en blanco) */
                    Matcher noEspacio = obtenerMatcher(RegularExpresions.caracterNoEspacio, grupo1);
                    boolean encontroCaracterNoEspacio = noEspacio.find();
                    if (encontroCaracterNoEspacio) {
                        
                        ErrorValidacion error = new ErrorValidacion(ErrorValidacion.ERROR_DECLARACION_XML, grupo1, linea, "No puede haber caracteres antes de la declaracion XML");
                        throw error;
                    }
                }

                if (!grupo3.equals("")) {
                    /* Aca lo que hacemos es validar que haya solamente
                     * espacios en blanco despues de la declaracion
                     *                                                    "\\S+"          (linea en blanco) */
                    Matcher noEspacio = obtenerMatcher(RegularExpresions.caracterNoEspacio, grupo3);
                    boolean encontroCaracterNoEspacio = noEspacio.find();
                    if (encontroCaracterNoEspacio) {
                        
                        ErrorValidacion error = new ErrorValidacion(ErrorValidacion.ERROR_DECLARACION_XML, grupo1, linea, "No puede haber caracteres despues de la declaracion XML");
                        throw error;
                    }
                }

                // RegularExpresions.declaracionXMLCompleta = <\\?xml\\s*version=\"\\d+\\.\\d+\"\\s*(encoding=\"UTF-(8|16)\")?\\s*(standalone=\"(no|yes)\")?\\s*\\?>
                Matcher matcherDeclaracionXMLCompleta = obtenerMatcher(RegularExpresions.declaracionXMLCompleta, linea.getLine());
                boolean encontroPatronDeclaracionXMLCompleta = matcherDeclaracionXMLCompleta.find();
                if (!encontroPatronDeclaracionXMLCompleta) {
                    
                    ErrorValidacion error = new ErrorValidacion(ErrorValidacion.ERROR_DECLARACION_XML, null, linea, null);
                    throw error;
                }

                if (resultadoValidacionDeclaracionXML) {
                    
                    ErrorValidacion error = new ErrorValidacion(ErrorValidacion.ERROR_DECLARACION_XML, null, linea, "Hay mas de una declaracion XML");
                    throw error;
                }

                resultadoValidacionDeclaracionXML = true;
            } else {
                if (VentanaPrincipal.chkValidarXml.isSelected()) {
                    if (!resultadoValidacionDeclaracionXML) {
                        
                        ErrorValidacion error = new ErrorValidacion(ErrorValidacion.ERROR_DECLARACION_XML, null, linea, "LA DECLARACION XML DEBE FIGURAR EN PRIMER LUGAR");
                        throw error;
                    }
                }
            }
        }

        return esDeclaracionXML;
    }

    /**
     * Esta funcion es utilizada para validar la declaracion DOCTYPE
     * <!DOCTYPE rootElement PUBLIC "PublicIdentifier" "URIreference">
     * @param linea - el renglon que se va analizar
     */
    public static boolean validarDeclaracionDocType(Linea linea) throws ErrorValidacion {
        boolean esDeclaracionDocType = false;

        if (!validarEspacioEnBlanco(linea)) {
            // RegularExpresions.declaracionDocType = (.*)<!DOCTYPE(.*)*>(.*)
            Matcher matcherDeclaracionDocType = obtenerMatcher(RegularExpresions.declaracionDocType, linea.getLine());
            boolean encontroPatronDeclaracionDocType = matcherDeclaracionDocType.find();

            if (encontroPatronDeclaracionDocType) {
                /* Si entra en este if es porque la linea corresponde a una declaracion !DOCTYPE
                 */

                esDeclaracionDocType = true;

                String grupo1 = matcherDeclaracionDocType.group(1);
                String grupo3 = matcherDeclaracionDocType.group(3);

                if (!grupo1.equals("")) {
                    /* Aca lo que hacemos es validar que haya solamente
                     * espacios en blanco antes de la declaracion
                     */
                    Matcher noEspacio = obtenerMatcher(RegularExpresions.caracterNoEspacio, grupo1);
                    boolean encontroCaracterNoEspacio = noEspacio.find();
                    if (encontroCaracterNoEspacio) {
                        
                        ErrorValidacion error = new ErrorValidacion(ErrorValidacion.ERROR_DECLARACION_XML, grupo1, linea, "No puede haber caracteres antes de la declaracion DOCTYPE");
                        throw error;
                    }
                }

                if (!grupo3.equals("")) {
                    /* Aca lo que hacemos es validar que haya solamente
                     * espacios en blanco despues de la declaracion
                     */
                    Matcher noEspacio = obtenerMatcher(RegularExpresions.caracterNoEspacio, grupo3);
                    boolean encontroCaracterNoEspacio = noEspacio.find();
                    if (encontroCaracterNoEspacio) {
                        
                        ErrorValidacion error = new ErrorValidacion(ErrorValidacion.ERROR_DECLARACION_XML, grupo3, linea, "No puede haber caracteres despues de la declaracion DOCTYPE");
                        throw error;
                    }
                }

                // RegularExpresions.declaracionDocTypeCompleta = <!DOCTYPE(\\s+)(\\w|\\.|-)*(\\s+PUBLIC|SYSTEM\\s+)?(\\s+\".+\"){0,2}\\s*>
                Matcher matcherDeclaracionDocTypeCompleta = obtenerMatcher(RegularExpresions.declaracionDocTypeCompleta, linea.getLine());
                boolean encontroPatronDeclaracionDocTypeCompleta = matcherDeclaracionDocTypeCompleta.find();
                if (!encontroPatronDeclaracionDocTypeCompleta) {
                    
                    ErrorValidacion error = new ErrorValidacion(ErrorValidacion.ERROR_DECLARACION_XML, null, linea, null);
                    throw error;
                }

                if (resultadoValidacionDeclaracionDocType) {
                    
                    ErrorValidacion error = new ErrorValidacion(ErrorValidacion.ERROR_DECLARACION_XML, null, linea, "Hay mas de una declaracion DOCTYPE");
                    throw error;
                }

                resultadoValidacionDeclaracionDocType = true;
            } else {
                if (VentanaPrincipal.chkValidarDoctype.isSelected()) {
                    if (!resultadoValidacionDeclaracionDocType) {
                        
                        ErrorValidacion error = new ErrorValidacion(ErrorValidacion.ERROR_DECLARACION_XML, null, linea, "Falta la declaracion DOCTYPE");
                        throw error;
                    }
                }
            }

        }

        return esDeclaracionDocType;
    }

    /**
     * Esta funcion se utiliza para obtener un Matcher enviando una expresion
     * regular y la linea de texto como parametros
     * @param er - Es un String que representa la expresion regular
     * @param line - Es un String que representa la linea de texto del XML
     * @return
     */
    public static Matcher obtenerMatcher(String er, String line) {
        Pattern regex = Pattern.compile(er, Pattern.CANON_EQ); //habilita equivalencia canónica
        Matcher regexMatcher = regex.matcher(line); //compara con el patron
        return regexMatcher;
    }

    /**
     * Metodo utilizado para inicializar los atributos de la clase Validaciones
     */
    public static void inicializar() {
        conjuntoEtiquetas = new ArrayList<EtiquetaXML>();
        comentario = false;
        comentarioFinal = false;
        validacionCorrecta = true;
        resultadoValidacionDeclaracionXML = false;
        resultadoValidacionDeclaracionDocType = false;
    }

    public static void mostrarMensajeErrorValidacion(ErrorValidacion errorException) {
        String errorValidacion = errorException.getError();
        ErrorValidacion.aux_contador++;
        int aux2 = ErrorValidacion.aux_contador % 2;
        VentanaPrincipal.txtConsola.setForeground(Color.RED);
        if (aux2 == 0) {
            VentanaPrincipal.imprimirMensajeConsola(errorValidacion + "\n");
        } else {
            VentanaPrincipal.imprimirMensajeConsola(errorValidacion + "\n");
        }
    }
}
